﻿using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Flux.Data;
using Flux.Data.Layers;
using Flux.Data.Maps;
using Flux.Data.Tilesets;
using Flux.Data.Utilities;
using Flux.Editor.Dialogs;
using Flux.Editor.Messaging;
using Flux.Editor.Models;
using Flux.Editor.Panes;
using Flux.Editor.ViewModels.PropertyViewModels;
using Flux.Mvvm.Commands;
using Flux.Mvvm.ViewModels;
using Microsoft.Win32;

namespace Flux.Editor.ViewModels
{
	public class MainWindowViewModel
		: ViewModel
	{
		#region Dependency Properties

		private ObservableCollection<PaneModel> _panes = new ObservableCollection<PaneModel>();
		public ObservableCollection<PaneModel> Panes
		{
			get { return _panes; }
			set
			{
				if (_panes == value) return;
				_panes = value;
				NotifyPropertyChanged();
			}
		}

		private PaneModel _activePane;
		public PaneModel ActivePane
		{
			get { return _activePane; }
			set
			{
				if (Equals(_activePane, value)) return;
				_activePane = value;
				NotifyPropertyChanged();
			}
		}

		private Project _activeProject;
		public Project ActiveProject
		{
			get { return _activeProject; }
			set
			{
				_activeProject = value;
				App.Project = value;
				NotifyPropertyChanged();
			}
		}

		private IPropertyViewModel _selectedObject;
		public IPropertyViewModel SelectedObject
		{
			get { return _selectedObject; }
			set
			{
				_selectedObject = value;
				NotifyPropertyChanged();
			}
		}

		private Map _activeMap;
		public Map ActiveMap
		{
			get { return _activeMap; }
			set
			{
				_activeMap = value;
				App.SelectedMap = value;
				NotifyPropertyChanged();
			}
		}

		private Brush _selectedLeftTile;
		public Brush SelectedLeftTile
		{
			get { return _selectedLeftTile; }
			set
			{
				_selectedLeftTile = value;
				NotifyPropertyChanged();
			}
		}

		private Brush _selectedRightTile;
		public Brush SelectedRightTile
		{
			get { return _selectedRightTile; }
			set
			{
				_selectedRightTile = value;
				NotifyPropertyChanged();
			}
		}

		private Tileset _selectedTileset;
		public Tileset SelectedTileset
		{
			get { return _selectedTileset; }
			set
			{
				_selectedTileset = value;
				NotifyPropertyChanged();
			}
		}
		
		#endregion

		#region Commands

		public ICommand NewProjectCommand { get; set; }
		public ICommand OpenProjectCommand { get; set; }
		public ICommand SaveProjectCommand { get; set; }

		public ICommand StartCommand { get; set; }
		public ICommand ConfigCommand { get; set; }
		public ICommand ExitCommand { get; set; }

		#endregion

		#region Initialization

		public MainWindowViewModel()
		{
			RegisterCommands();
			RegisterMessages();
		}

		private void RegisterCommands()
		{
			NewProjectCommand = new DelegateCommand(NewProject);
			OpenProjectCommand = new DelegateCommand(OpenProject);
			SaveProjectCommand = new DelegateCommand(SaveProject);

			StartCommand = new DelegateCommand(Start);
			ConfigCommand = new DelegateCommand(Configure);
			ExitCommand = new DelegateCommand(Exit);
		}

		private void RegisterMessages()
		{
			App.Messenger.Register(Tokens.NewProject, NewProject);
			App.Messenger.Register(Tokens.OpenProject, OpenProject);
			App.Messenger.Register(Tokens.SaveProject, SaveProject);
			App.Messenger.Register(Tokens.SetSelectedObject, SetSelectedObject);
			App.Messenger.Register(Tokens.AddNewMap, AddNewMap);
			App.Messenger.Register(Tokens.AddExistingMap, AddExistingMap);
			App.Messenger.Register(Tokens.AddNewTileset, AddNewTileset);
			App.Messenger.Register(Tokens.UpdateSelectedTileset, UpdateSelectedTileset);
			App.Messenger.Register(Tokens.UpdateLeftSelectedTile, UpdateLeftSelectedTile);
			App.Messenger.Register(Tokens.UpdateRightSelectedTile, UpdateRightSelectedTile);
			App.Messenger.Register(Tokens.SetActiveMap, SetActiveMap);
			App.Messenger.Register(Tokens.AddTileLayer, AddTileLayer);
		}

		private void Initialize()
		{
			SelectedLeftTile = new SolidColorBrush(Color.FromRgb(63, 88, 127));
			SelectedRightTile = new SolidColorBrush(Color.FromRgb(63, 88, 127));
		}

		#endregion

		#region Pane Methods

		public PaneModel AddPane(PaneTypes paneType)
		{
			PaneModel pane = new StartPageModel();
			switch (paneType)
			{
				case PaneTypes.StartPage:
					if (ContainsPaneType(paneType)) return null;
					pane = new StartPageModel();
					Initialize();
					break;
				case PaneTypes.Map:
					var map = ((MapPropertyViewModel) SelectedObject).DataContext;
					pane = new MapPaneModel(map);
					ActiveMap = map;
					break;
			}
			Panes.Add(pane);
			return pane;
		}

		public void RemovePane(PaneModel pane)
		{
			Panes.Remove(pane);
			if (Panes.Count > 0) ActivePane = Panes[0];
			else ActivePane = null;
		}

		public bool ContainsPaneType(PaneTypes paneType)
		{
			foreach (var pane in Panes)
			{
				switch (paneType)
				{
					case PaneTypes.StartPage:
						if (pane is StartPageModel) return true;
						break;
				}
			}
			return false;
		}

		private PaneModel GetPane(PaneTypes paneType)
		{
			foreach (var pane in Panes)
			{
				switch (paneType)
				{
					case PaneTypes.StartPage:
						if (pane is StartPageModel) return pane;
						break;
				}
			}
			return null;
		}

		#endregion

		#region App Commands

		public void Start(object message = null)
		{
			AddPane(PaneTypes.StartPage);
		}

		public void Configure(object message = null)
		{
			
		}

		public void Exit(object message = null)
		{
			App.Current.MainWindow.Close();
		}

		#endregion

		#region Project Actions

		public void NewProject(object message = null)
		{
			var dialog = new NewProjectDialog
			{
				Owner = App.Current.MainWindow
			};
			var results = dialog.ShowDialog();
			if (results == true)
			{
				var projectName = dialog.ProjectName;
				var tileWidth = dialog.TileWidth;
				var tileHeight = dialog.TileHeight;

				var project = new Project
				{
					Name = projectName,
					TileWidth = tileWidth,
					TileHeight = tileHeight
				};

				ActiveProject = project;
				SelectedObject = GetPropertyViewModel(ActiveProject);

				if (message is StartPageViewModel)
				{
					var paneModel = GetPane(PaneTypes.StartPage);
					if (paneModel != null)
					{
						RemovePane(paneModel);
					}
				}
			}
		}

		public void OpenProject(object message = null)
		{
			var dialog = new OpenFileDialog();
			var results = dialog.ShowDialog();
			if (results == true)
			{
				var filename = dialog.FileName;
				if (message is StartPageViewModel)
				{
					var paneModel = GetPane(PaneTypes.StartPage);
					if (paneModel != null)
					{
						RemovePane(paneModel);
					}
				}
			}
		}

		public void SaveProject(object message = null)
		{
			var dialog = new SaveFileDialog();
			var results = dialog.ShowDialog();
			if (results == true)
			{
				var filename = dialog.FileName;

			}
		}

		#endregion

		#region Map Actions

		public void AddNewMap(object message = null)
		{
			var dialog = new NewMapDialog
			{
				Owner = App.Current.MainWindow
			};
			var results = dialog.ShowDialog();
			if (results == true)
			{
				var mapName = dialog.Name;
				var mapWidth = dialog.Width;
				var mapHeight = dialog.Height;
				var tileWidth = ActiveProject.TileWidth;
				var tileHeight = ActiveProject.TileHeight;

				var map = new Map
				{
					Name = mapName,
					Width = mapWidth,
					Height = mapHeight,
					TileWidth = tileWidth,
					TileHeight = tileHeight
				};

				var tileLayer = new TileLayer(mapWidth, mapHeight)
				{
					Name = "Tile Layer 1",
					TileWidth = tileWidth,
					TileHeight = tileHeight
				};

				map.Layers.Add(tileLayer);

				ActiveProject.Maps.Add(map);

				SelectedObject = GetPropertyViewModel(map);
				AddPane(PaneTypes.Map);
				App.SelectedLayer = tileLayer;
				App.Messenger.Send(Tokens.SwitchUtilityTab, "Layers");
				App.Messenger.Send(Tokens.RefreshProjectExplorer, null);
				App.Messenger.Send(Tokens.RefreshLayersPanel, null);
				App.Messenger.Send(Tokens.SelectLayer, tileLayer);
			}
		}

		public void AddExistingMap(object message = null)
		{
			var dialog = new OpenFileDialog();
			var results = dialog.ShowDialog();
			if (results == true)
			{
				var filename = dialog.FileName;
			}
		}

		public void SetActiveMap(object message = null)
		{
			var map = (Map) message;
			ActiveMap = map;
		}

		#endregion

		#region Tileset Actions

		public void AddNewTileset(object message = null)
		{
			var dialog = new NewTilesetDialog
			{
				Owner = App.Current.MainWindow
			};
			var results = dialog.ShowDialog();
			if (results == true)
			{
				var tileset = new Tileset
				{
					Name = dialog.TilesetName,
					ImagePath = dialog.ImagePath,
					TileWidth = dialog.TileWidth,
					TileHeight = dialog.TileHeight
				};
				ActiveProject.Tilesets.Add(tileset);
				SelectedObject = GetPropertyViewModel(tileset);
				App.Messenger.Send(Tokens.RefreshProjectExplorer, null);
				App.Messenger.Send(Tokens.RefreshTilesetPanel, tileset);
			}
		}

		public void UpdateSelectedTileset(object message)
		{
			SelectedTileset = (Tileset)message;
			App.SelectedTileset = SelectedTileset;
		}

		public void UpdateLeftSelectedTile(dynamic tilesetInfo)
		{
			var x = tilesetInfo.X;
			var y = tilesetInfo.Y;
			var w = SelectedTileset.TileWidth;
			var h = SelectedTileset.TileHeight;

			var bmp = new BitmapImage(new Uri(SelectedTileset.ImagePath, UriKind.RelativeOrAbsolute));

			var brush = new ImageBrush(bmp)
			{
				ViewboxUnits = BrushMappingMode.Absolute,
				ViewportUnits = BrushMappingMode.Absolute,
				Viewport = new Rect(0, 0, 48, 48),
				Viewbox = new Rect(x * w, y * h, w, h),
				Stretch = Stretch.Uniform,
				TileMode = TileMode.None,
			};

			SelectedLeftTile = brush;
			App.SelectedLeftTile = TileHelper.GetIndex(x, y, w);
		}

		public void UpdateRightSelectedTile(dynamic tilesetInfo)
		{
			var x = tilesetInfo.X;
			var y = tilesetInfo.Y;
			var w = SelectedTileset.TileWidth;
			var h = SelectedTileset.TileHeight;

			var bmp = new BitmapImage(new Uri(SelectedTileset.ImagePath, UriKind.RelativeOrAbsolute));

			var brush = new ImageBrush(bmp)
			{
				ViewboxUnits = BrushMappingMode.Absolute,
				ViewportUnits = BrushMappingMode.Absolute,
				Viewport = new Rect(0, 0, 48, 48),
				Viewbox = new Rect(x * w, y * h, w, h),
				Stretch = Stretch.Uniform,
				TileMode = TileMode.None,
			};

			SelectedRightTile = brush;
			App.SelectedRightTile = TileHelper.GetIndex(x, y, w);
		}

		#endregion

		#region Layer Actions

		public void AddTileLayer(object message = null)
		{
			if (ActiveProject == null) return;
			if (ActiveMap == null) return;

			var layer = new TileLayer(ActiveMap.Width, ActiveMap.Height)
			{
				Name = "Tile Layer"
			};

			ActiveMap.AddLayer(layer.Name, layer);
			App.Messenger.Send(Tokens.RefreshLayersPanel, null);
			App.Messenger.Send(Tokens.RefreshProjectExplorer, null);
			App.Messenger.Send(Tokens.SelectLayer, layer);
		}

		#endregion

		#region Methods

		private IPropertyViewModel GetPropertyViewModel<T>(T input)
		{
			if (input is Project)
			{
				return new ProjectPropertyViewModel(input as Project);
			}
			if (input is Map)
			{
				return new MapPropertyViewModel(input as Map);
			}
			if (input is Tileset)
			{
				return new TilesetPropertyViewModel(input as Tileset);
			}
			return null;
		}

		public void SetSelectedObject(object message = null)
		{
			var propertyViewModel = GetPropertyViewModel(message);
			SelectedObject = propertyViewModel;
		}

		#endregion
	}
}